package filter;

import co.unal.visor.model.Image;
import co.unal.visor.util.Pixel;
import co.unal.visor.util.RGB;
import co.unal.visor.view.Lienzo;
import filter.util.Matrix;



public class Transformation2D extends Transformation {

	/**
	 * rota una imagen con respecto centro del lienzo en pantalla como eje de
	 * rotacion, en el sentido contrario a las manecillas del reloj. La funcion
	 * simplemente multiplica las coordenadas de la posicion de cada pixel en
	 * img, y la coordenada resultante se usa para dibujar el pixel en
	 * pixelmatrix.
	 * 
	 * @param img
	 * @param grades
	 * @param lienzo
	 * @return
	 */
	////////////////////////////////////////////////////////////////
	/**/
	//cambiar implementacion
	public static void rotateCenteredImage(Image img, float grades,
			Lienzo lienzo) {
		// constantes de la funcion
		int ancholienzo = lienzo.getLPixelMatrix()[0].length;
		int altolienzo = lienzo.getLPixelMatrix().length;
		int dx = (int) (ancholienzo - img.getWidth()) / 2;
		int dy = (int) (altolienzo - img.getHeight()) / 2;

		for (int i = 0; i < altolienzo; i++) {
			lienzo.LPixelMatrix[i] = new Pixel[ancholienzo];
			for (int j = 0; j < ancholienzo; j++) {
				lienzo.LPixelMatrix[i][j] = new RGB();
			}
		}

		Pixel[][] temp=img.createPixelMatrix();
		
		for (int i = 0; i <temp.length; i++) {
			for (int j = 0; j <temp[0].length; j++) {
				double[][] newcoord = new double[][] { { j }, { i }, { 1 } };

				// translada al centro

				newcoord = Matrix.multiplyMatrix(
						returnTranslationMatrix(-(int) img.getWidth() / 2,
								-(int) img.getHeight() / 2), newcoord);

				// rota la coordenada

				newcoord = Matrix.multiplyMatrix(
						returnRotationMatrix((float) Math.toRadians(grades)),
						newcoord);

				// translada de nuevo a la posicion original

				newcoord = Matrix.multiplyMatrix(
						returnTranslationMatrix((int) img.getWidth() / 2,
								(int) img.getHeight() / 2),
								newcoord);

				// translada nuevamente, al centro del lienzo (evade indices de
				// posicion negativos)

				newcoord = Matrix.multiplyMatrix(
						returnTranslationMatrix(dx, dy), newcoord);

				//aqui se hace la interpolacion (por ahora, solo vecino mas cercano. 
				//opcional)
				int np= nearestPointInterpolation(newcoord);
				lienzo.LPixelMatrix[(int) newcoord[1][0]][(int) newcoord[0][0]] = new RGB(
						temp[i][j].getRGB());
				if(np==1){
					lienzo.LPixelMatrix[(int) newcoord[1][0]+1][(int) newcoord[0][0]] = new RGB(
							temp[i][j].getRGB());
				}else{
					if(np==2){
						lienzo.LPixelMatrix[(int) newcoord[1][0]][(int) newcoord[0][0]+1] = new RGB(
								temp[i][j].getRGB());
						
					}else{
						if(np==3){
							lienzo.LPixelMatrix[(int) newcoord[1][0]+1][(int) newcoord[0][0]] = new RGB(
									temp[i][j].getRGB());
							lienzo.LPixelMatrix[(int) newcoord[1][0]][(int) newcoord[0][0]+1] = new RGB(
									temp[i][j].getRGB());
							
						}
					}
				}
				}
		}
	}

	private static int nearestPointInterpolation(double[][] newcoord) {
		int ret=0;
		// interpola la componente x
		if (newcoord[1][0] % 1 != 0) {
			newcoord[1][0] = Math.floor(newcoord[1][0]);
			ret+=1;
		} 
		// interpola la componente y
		if (newcoord[0][0] % 1 != 0) {
			newcoord[0][0] = Math.floor(newcoord[0][0]);
		   ret+=2;
		} 
//		else {
//			newcoord[0][0] = Math.ceil(newcoord[0][0]);
//		}
		return ret;
	}

	/**
	 * funcion a la que se ingresa una rotacion en grados, y retorna la matriz
	 * de rotacion asociada
	 * 
	 * @param grades
	 * @return
	 */
	private static double[][] returnRotationMatrix(float grades) {
		//int num_aprox=15;
		double[][] retorno = new double[][] {
				{ Math.cos(grades), -Math.sin(grades), 0 },
				{ Math.sin(grades), Math.cos(grades), 0 },
				{ 0, 0, 1 } };
		/*double[][] retorno = new double[][] {
				{ MyMath.cos(grades,num_aprox), -MyMath.sin(grades,num_aprox), 0 },
				{ MyMath.sin(grades,num_aprox), MyMath.cos(grades,num_aprox), 0 },
				{ 0, 0, 1 } };*/
		return retorno;
	}

	private static double[][] returnTranslationMatrix(int dx, int dy) {
		double[][] retorno = new double[][] { { 1, 0, dx }, { 0, 1, dy },
				{ 0, 0, 1 } };
		return retorno;
	}
}
